﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Lidgren.Network;
using NetBrick.Common;
using NetBrick.Server;
using UniBrickCommon;
using UniBrickCommon.Packets;
using UniBrickServer.Handler;
using UniBrickServer.Utils;
using UnityEngine;

namespace UniBrickServer
{
    public class UniBrickServer : Server
    {
        private Thread _updateThread;

        private UniBrickServer() : base(CommonConstants.AppName, CommonConstants.Port, 100)
        {
        }

        public static UniBrickServer Instance { get; private set; }

        public Dictionary<string, UniBrickPeer> Players { get; private set; }

        public static void Main(string[] args)
        {
            Instance = new UniBrickServer();
        }

        protected override void Init()
        {
            Log.Info("Initializing UniBrick Server...");
            Log.Info("Registering packets...");
            RegisterPackets();
            Log.Info("->OK");
            Log.Info("Registering packet handlers...");
            RegisterPacketHandlers();
            Log.Info("->OK");
            _updateThread = new Thread(() =>
            {
                while (!ShuttingDown)
                {
                    if (Peers.Count == 0) continue;
                    var playerList =
                        new List<string>(from peer in Peers.Values
                            where ((UniBrickPeer) peer).Authenticated
                            select ((UniBrickPeer) peer).Name);
                    var updatePlayerListPacket = new UpdatePlayerListPacket(playerList);
                    BroadcastPacketToAuthenticated(UniBrickPacketCode.UpdatePlayerList, updatePlayerListPacket,
                        NetDeliveryMethod.ReliableOrdered);

                    Thread.Sleep(1000);
                }
            });
            _updateThread.Start();
            new Thread(() =>
            {
                while (!ShuttingDown)
                {
                    var readLine = Console.ReadLine();
                    if (readLine == null) continue;
                    var command = readLine;
                    var commandName = command.Split(' ')[0].ToLower();
                    var commandArgs = new string[0];
                    if (command.Split(' ').Count() > 1)
                        commandArgs = command.Substring(commandName.Length + 1).Split(' ');

                    switch (commandName)
                    {
                        case "createuser":
                            if (commandArgs.Count() == 2)
                            {
                                var userData = new Dictionary<byte, object>
                                {
                                    {
                                        ServerConstants.UserdataPosition,
                                        new Vector3(-80, 50, -32).ToVectorString()
                                    },
                                    {
                                        ServerConstants.UserdataRotation,
                                        Quaternion.identity.ToQuaternionString()
                                    },
                                    {ServerConstants.UserdataWorld, WorldName.BrickIsland},
                                    {ServerConstants.UserdataFriendList, new string[25]}
                                };
                                PlayerUtils.CreateUser(commandArgs[0], commandArgs[1], userData);
                            }
                            else
                            {
                                Log.Warn("Too many/few arguments.");
                            }
                            break;
                        case "deleteuser":
                            if (commandArgs.Count() != 1)
                            {
                                Log.Warn("Too many/few arguments.");
                            }
                            else if (!PlayerUtils.UserExists(commandArgs[0]))
                            {
                                Log.Warn("User doesn't exist.");
                            }
                            else
                                PlayerUtils.DeleteUser(commandArgs[0]);
                            break;
                    }
                }
            }).Start();
            AppDomain.CurrentDomain.ProcessExit += new EventHandler((s, e) =>
            {
                Shutdown();
            });
            DatabaseUtils.Init();
            Players = new Dictionary<string,UniBrickPeer>();
            Log.Info("Initialized UniBrick Server.");
        }

        private void RegisterPackets()
        {
            Packet.Instance.Register(UniBrickPacketCode.AuthenticateRequest, typeof (AuthenticatePacket.Request));
            Packet.Instance.Register(UniBrickPacketCode.AuthenticateResponse, typeof (AuthenticatePacket.Response));
            Packet.Instance.Register(UniBrickPacketCode.UpdateRequest, typeof (UpdatePacket.Request));
            Packet.Instance.Register(UniBrickPacketCode.UpdateEvent, typeof (UpdatePacket.Event));
            Packet.Instance.Register(UniBrickPacketCode.UpdatePlayerList, typeof (UpdatePlayerListPacket));
        }

        private void RegisterPacketHandlers()
        {
            AddPacketHandler(UniBrickPacketCode.AuthenticateRequest, new AuthenticateHandler(this));
            AddPacketHandler(UniBrickPacketCode.UpdateRequest, new UpdateHandler(this));
        }

        protected override BasePeer CreatePeer(NetConnection connection)
        {
            return new UniBrickPeer(connection);
        }

        public void BroadcastPacketToAuthenticated(ushort packetCode, object packetData,
            NetDeliveryMethod deliveryMethod)
        {
            var peers = new List<BasePeer>(from peer in Peers.Values
                where ((UniBrickPeer) peer).Authenticated
                select peer);
            if (peers.Count > 0)
                SendPacket(packetCode, packetData, peers, deliveryMethod);
        }
    }
}